CUBE CONNECT Edition Help

Usage of Lua Expressions within the CubeDatabase class

Lua (ref. lua.org) is a scripting language well renewed for being very fast, and it is the expression language adopted in the CubeAPI.

The usage of Lua expressions allows to avoid the need of looping through links or nodes in the network, allowing to undertake many different calculations or selection of links and nodes very efficiently. The network API supports the usage of Lua expressions within the following methods when processing CUBE networks:

  • value(s)ForLinks()
  • value(s)ForNodes()
  • valueSum(s)ForLinks(), valueAverage(s)ForLinks()
  • valueSum(s)ForNodes(), valueAverage(s)ForNodes()
  • addLinkAttribute(s)ByExpression(s)(), updateLinkAttribute(s)ByExpression(s)()
  • addNodeAttributeByExpression(s)(), updateNodeAttribute(s)ByExpression(s)()

The Lua expression is entered as a single or multi line string, for single entrances, or as a string vector, for multi entrances.

Comments in Lua are entered anywhere with a double hyphen --.

NodeQuery and LinkQuery objects can also be used as a further argument in these methods, to define specific queries for links and nodes respectively.

Special read-only variables that can be used within the Lua expressions are the below:

  • maxZoneNumber is the maximum number for zones (centroids) in the network.
  • startingNodeNumber is the starting node number for the physical nodes in the network.
  • __numEntities is the number of entities processed, based on link or node queries, in the current result.
  • __entityIndex is the current entity index (starting at 0) within the implicit loop for the result set.
  • __expressionIndex is the expression index (starting at 0) for each expression in an expression list for the usage within valuesForNodes

Two special functions are provided:

  • The value() function (alias cost()) can be used within the Lua expression, and is evaluated for each element, like in an internal link or node loop.
  • The exclude() function allows to exclude a link or node from the value processing, allowing for advanced filtering.

The following special variables are available within the Lua expression:

  • link.a and link.b nodes for the link A and B nodes, and link.attribute_name for any other attribute of the link layer. .
  • node.n for the node number, and node.attribute_name for any other attribute of the node layer

The a, b and value for links, and n and value for nodes, are accessible in the returned object.

In addition to access the current link with the link variable, and its associated A and B nodes, it is also possible to use link_r to get the reverse link, with associated A and B nodes.

For both link and node processing, the following special variables can be extracted:

  • link.__incomingCount; node.__incomingCount
  • link.__incoming; node.__incoming
  • link.__outgoingCount; node.__outgoingCount
  • link.__outgoing; node.__outgoing

Where __incomingCount provides the number of incoming links to either the current link or node, and __incoming attribute provides a Lua table (associative array) with the incoming links to either the current link or node (same for outgoing). The user should notice that the incoming and outgoing links are also including the opposite direction in case of two-way links, differently than the linkConnectionReport method returning upstreamLinks and downstreamLinks not including the opposite direction.

A straightforward example to start understanding the usage of Lua expressions with these special functions is the calculation of the free-flow time in minutes from two attributes stored in the CUBE network, named DISTANCE and SPEED. In this example, the Lua expression could be the below, assuming distance in km and speed in km/h:

lua_expr = "value((link.distance / link.speed) * 60)"

The valueForLinks method returns a LinkValueEntryVector, which is a link vector storing the evaluated value above for each link.

val_fftime = db.valueForLinks(network_name, lua_expr)

The expression below will print A-node, B-node and the ff-time value for the link with index link_ix.

print(val_fftime[link_ix].a, val_fftime[link_ix].b, val_fftime[link_ix].value)
The db.valueForLinks() returns a LinkValueEntryVector, which is an iterable object, that can be looped through as in the example below.
for link_entry in val_fftime:
    print(link_entry.a, link_entry.b, link_entry.value)
In the case the ff-time should be added as an extra attribute in the network, then the addLinkAttributeByExpression method can be used.
db.addLinkAttributeByExpression(network_name, "fftime", cp.FieldType_Real, lua_expr)

This will add a Real type field named fftime to the processed network, storing the value calculated with the Lua expression for every link.

The below example shows the usage of the second special function exclude() and of the special variable maxZoneNumber. In the example, the centroid connectors are excluded from the calculation of the fftime value and therefore for the resulting vector.

lua_expr = """
             if link.a <= maxZoneNumber or link.b <= maxZoneNumber then 
                -- excluding centroid connectors from the output vector
                exclude()
             else
                -- calculating the free-flow time value
                value((link.distance / link.speed) * 60)
             end
           """
value_fftime_no_centr = db.valueForLinks(network_name, lua_expr)

The below example illustrates the use of link_r for extracting the reverse direction information, it also shows how to use lists to store multiple expressions and attribute names. Currently, there is no python list support for FieldTypes, therefore FieldTypeVector needs to be used.

lua_expr_list = [
    """
        if link_r ~= nil then
            -- nil means that the reverse link does not exist 
            -- i.e., link_r ~= nil means two-way links
            value(0)
        else 
            value(1)
        end
    """,
    """
        if link_r ~= nil then
           -- nil means that the reverse link does not exist 
           -- i.e., link_r ~= nil means two-way links
           value(link.cap + link_r.cap)
        else 
            value(link.cap)
        end
    """
    ]
attribute_names_list = ["oneway", "twowaycap"]
attribute_types_list = cp.FieldTypeVector()
attribute_types_list.append(cp.FieldType_Integer)
attribute_types_list.append(cp.FieldType_Real)
db.addLinkAttributesByExpressions(network_name, attribute_names_list, attribute_types_list, lua_expr_list)

In the example, two additional attributes are added to the link layer, "oneway" is 1 for one-way links and 0 for two-way links, "twowaycap" is the sum in the two directions of the directional capacities, representing the capacity of the entire link.

In case of using valuesForLinks (or valuesForNodes) method with a list of Lua expressions, the returned object will be indexed by the value and the entity, like in the example below (referring to the previous example expressions).

vals_for_links = db.valuesForLinks(network_name, lua_expr_list)
# oneway indicator value (first) for the link with index link_ix
print(vals_for_links[0][link_ix].value)
# twowaycapacity value (second) for the link with index link_ix
print(vals_for_links[1][link_ix].value)

These simple examples show how these expressions allow to undertake those type of calculations efficiently, avoiding the need for looping through links or nodes.

An additional feature is available to support more complex calculations within Lua expressions. It is indeed possible to retrieve a single real (double) or string value. This is achievable by using the following global variables within the Lua expression (10 real variables and 5 string variables):

__userDataReal1

...

__userDataReal10

__userDataString1

...

_userDataString5

The default value for those variables is 0.0 for __userDataReal# and “” for __userDataString#.

It is then possible to obtain the resulting variables values by calling the lastExpressionUserData() method, which returns the ExpressionUserData() struct with the calculated values after processing all the entities (e.g., lastExpressionUserData().real1), for:

real1

...

real10

string1

...

string5

An example is provided below. This example shows how to deal with Lua tables within the expression (for incoming links in the example), how to use the Lua printing capabilities, which is useful for debugging purposes and to view special variables, and finally how to define and calculate global variables within the Lua expression, and then retrieve the final value in the main Python script. Note: for key, value in pairs is the Lua syntax for looping through the table (associative array) storing data in key/value pair. Differently than Python, the implicit index starts from 1.

lua_expr = """
            incoming_count = node.__incomingCount
            incoming_vol_tot = 0
            if incoming_count > 0 then
                print("#######################################")
                print("node =", node.n)
                print("n. of incoming links =", incoming_count)
                for key, value in pairs(node.__incoming) do
                    incoming_vol = value.volumeAB
                    incoming_vol_tot = incoming_vol_tot + incoming_vol
                    print(">>", string.format("%5.0f", key), 
                                string.format("%5.0f", value.a), 
                                string.format("%5.0f", value.b),
                                string.format("%5.0f", incoming_vol), 
                                string.format("%5.0f", incoming_vol_tot))
                end
                __userDataReal1 = __userDataReal1 + incoming_vol_tot
            end
            value(incoming_vol_tot)
           """

# node_val = db.valueForNodes(network_name, lua_expr)
db.addNodeAttributeByExpression(network_name, "vol_inc", cp.FieldType_Real, lua_expr)

print("#######################################")
tot_incoming_vol =  db.lastExpressionUserData().real1
print("total incoming volume =", tot_incoming_vol)